//------------------------------------------------------------------------------
//  FileStream.cpp
//  (C) 2009 xoyojank
//------------------------------------------------------------------------------
#include "StdEddy.h"
#include "Common/FileStream.h"
#include "Common/FileSystem.h"

//------------------------------------------------------------------------------
namespace Eddy
{

//------------------------------------------------------------------------------
FileStream::FileStream()
    : isOpen(false)
    , mappedData(NULL)
    , handle(NULL)
    , accessMode(ReadAccess)
{
    // empty
}

//------------------------------------------------------------------------------
FileStream::FileStream(const WString& s, FileStream::AccessMode access)
    : isOpen(false)
    , mappedData(NULL)
    , handle(NULL)
    , accessMode(ReadAccess)
{
    this->Open(s, access);
}
//------------------------------------------------------------------------------
FileStream::~FileStream()
{
    if (this->IsOpen())
    {
        this->Close();
    }
}

//------------------------------------------------------------------------------
bool
FileStream::Open(const WString& s, FileStream::AccessMode access)
{
    assert(!this->IsOpen());
    assert(!s.empty());

    FileSystem::ToFullPath(this->path, s);
    this->accessMode = access;
    DWORD accessFlag = 0;
    DWORD shareMode = 0;
    DWORD disposition = 0;
    switch (this->accessMode)
    {
    case ReadAccess:
        accessFlag = GENERIC_READ;
        shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
        disposition = OPEN_EXISTING;
        break;
    case WriteAccess:
        accessFlag = GENERIC_WRITE;
        shareMode = FILE_SHARE_READ;
        disposition = CREATE_ALWAYS;
        break;
    case ReadWriteAccess:
        accessFlag = GENERIC_READ | GENERIC_WRITE;
        shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
        disposition = OPEN_ALWAYS;
        break;
    }
    this->handle = CreateFile(this->path.c_str(), accessFlag, shareMode, 0,
                              disposition, FILE_FLAG_SEQUENTIAL_SCAN, NULL);

    this->isOpen = (INVALID_HANDLE_VALUE != this->handle);
    if (!this->isOpen)
    {
        this->handle = NULL;
        return false;
    }

    return true;
}

//------------------------------------------------------------------------------
void
FileStream::Close()
{
    assert(this->IsOpen());
    assert(NULL != this->handle);

    if (this->IsMapped())
    {
        this->UnMap();
    }

    CloseHandle(this->handle);
    this->handle = NULL;
    this->isOpen = false;
}

//------------------------------------------------------------------------------
SizeT
FileStream::GetSize() const
{
    assert(this->IsOpen());
    assert(NULL != this->handle);

    return GetFileSize(this->handle, NULL);
}

//------------------------------------------------------------------------------
IndexT
FileStream::GetPosition() const
{
    assert(this->IsOpen());
    assert(NULL != this->handle);

    return SetFilePointer(handle, 0, NULL, FILE_CURRENT);
}

//------------------------------------------------------------------------------
void
FileStream::Write(const void* ptr, SizeT numBytes)
{
    assert(this->IsOpen());
    assert(NULL != this->handle);
    assert(NULL != ptr);
    assert(numBytes > 0);

    DWORD bytesWritten;
    BOOL result = WriteFile(this->handle, ptr, numBytes, &bytesWritten, NULL);
    assert((TRUE == result) && (numBytes == bytesWritten));
}

//------------------------------------------------------------------------------
SizeT
FileStream::Read(void* ptr, SizeT numBytes)
{
    assert(this->IsOpen());
    assert(NULL != this->handle);
    assert(NULL != ptr);
    assert(numBytes > 0);

    DWORD bytesRead;
    BOOL result = ReadFile(this->handle, ptr, numBytes, &bytesRead, NULL);
    assert(TRUE == result);
    return bytesRead;
}

//------------------------------------------------------------------------------
void
FileStream::Seek(IndexT offset, FileStream::SeekMethod method)
{
    assert(this->IsOpen());
    assert(NULL != this->handle);

    SetFilePointer(this->handle, offset, NULL, method);
}

//------------------------------------------------------------------------------
bool
FileStream::Eof() const
{
    assert(this->IsOpen());
    assert(NULL != this->handle);

    DWORD fpos = SetFilePointer(this->handle, 0, NULL, FILE_CURRENT);
    DWORD size = GetFileSize(this->handle, NULL);
    return fpos >= size;
}

//------------------------------------------------------------------------------
void*
FileStream::Map()
{
    assert(this->IsOpen());
    assert(NULL != this->handle);
    assert(!this->IsMapped());

    SizeT size = this->GetSize();
    this->mappedData = new byte[size];
    this->Seek(0, Begin);
    SizeT readSize = this->Read(this->mappedData, size);
    assert(readSize == size);
    return this->mappedData;
}

//------------------------------------------------------------------------------
void
FileStream::UnMap()
{
    assert(this->IsOpen());
    assert(NULL != this->handle);
    assert(this->IsMapped());

    SafeDelArray(this->mappedData);
}

}// Eddy
